/**
 * @author:cavion(曹仁道)
 * @email:caorendao187@163.com
 * 2016年10月26日
 */
package org.rk.core.common.util;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.FatalBeanException;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;

import com.esotericsoftware.reflectasm.MethodAccess;
/**
 * @author:cavion
 * @描述：实体处理工具类
 * 2016年10月26日 下午9:29:11
 */
public class RKBeanUtil  {

	private static Map<String, MethodAccess> beanMethodcache = new HashMap<String, MethodAccess>();

	private static int beanMethodcacheCapacity = 1000;

	private static MethodAccess getFromBeanMethodcache(Class<?> type) {
		if (beanMethodcache == null) {
			beanMethodcache = new HashMap<String, MethodAccess>();
		}
		String key = type.getName();
		MethodAccess access = beanMethodcache.get(key);
		if (access == null) {
			access = MethodAccess.get(type);
			if (access != null) {
				if (beanMethodcache.size() <= beanMethodcacheCapacity) {
					beanMethodcache.put(key, access);
				}
			}
		}
		return access;
	}


	public static void copy(Object source, Object target, Boolean ignorNull) throws BeansException {
		copy(source, target, ignorNull, null, (String[]) null);
	}

	private static void copy(Object source, Object target, Boolean ignorNull, Class<?> editable,
			String... ignoreProperties) throws BeansException {

		Assert.notNull(source, "Source must not be null");
		Assert.notNull(target, "Target must not be null");

		Class<?> actualEditable = target.getClass();
		if (editable != null) {
			if (!editable.isInstance(target)) {
				throw new IllegalArgumentException("Target class [" + target.getClass().getName()
						+ "] not assignable to Editable class [" + editable.getName() + "]");
			}
			actualEditable = editable;
		}
		PropertyDescriptor[] targetPds = BeanUtils.getPropertyDescriptors(actualEditable);
		List<String> ignoreList = (ignoreProperties != null ? Arrays.asList(ignoreProperties) : null);

		for (PropertyDescriptor targetPd : targetPds) {
			Method writeMethod = targetPd.getWriteMethod();
			if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) {
				PropertyDescriptor sourcePd = BeanUtils.getPropertyDescriptor(source.getClass(), targetPd.getName());
				if (sourcePd != null) {
					Method readMethod = sourcePd.getReadMethod();
					if (readMethod != null && ClassUtils.isAssignable(writeMethod.getParameterTypes()[0],
							readMethod.getReturnType())) {
						try {
							if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
								readMethod.setAccessible(true);
							}
							Object value = readMethod.invoke(source);
							if (ignorNull != null && ignorNull) {
								// if(value!=null){//空对象 或者空集合
								if (value != null && (!"[]".equals(value.toString()))) {// 空对象
																						// 或者空集合
									if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
										writeMethod.setAccessible(true);
									}
									writeMethod.invoke(target, value);
								}
							} else {
								if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
									writeMethod.setAccessible(true);
								}
								writeMethod.invoke(target, value);
							}

						} catch (Throwable ex) {
							throw new FatalBeanException(
									"Could not copy property '" + targetPd.getName() + "' from source to target", ex);
						}
					}
				}
			}
		}
	}

	@SuppressWarnings("unchecked")
	public static List<?> removeDuplicate(List<?> list) {
		@SuppressWarnings("rawtypes")
		List<?> listWithoutDup = new ArrayList(new HashSet(list));
		return listWithoutDup;
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	public static List<?> removeDuplicateWithOrder(List list) {
		if (list == null || list.size() == 0) {
			return list;
		}
		List newList = new ArrayList();
		for (Object object : list) {
			if (!newList.contains(object)) {
				newList.add(object);
			}
		}
		return newList;
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	public static Object[] removeDuplicateWithOrder(Object[] array) {
		if (array == null || array.length == 0) {
			return array;
		}
		List newList = new ArrayList();
		for (Object object : array) {
			if (!newList.contains(object)) {
				newList.add(object);
			}
		}
		Class elementType = array.getClass().getComponentType();
		Object[] newArray = (Object[]) java.lang.reflect.Array.newInstance(elementType, 0);
		array = newList.toArray(newArray);
		return array;
	}

	@SuppressWarnings("rawtypes")
	public static Object convertMapToBean(Map map, Class<?> type) {
		Object obj = null;
		try {
			BeanInfo beanInfo = null;
			beanInfo = Introspector.getBeanInfo(type);
			obj = type.newInstance();
			PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
			for (PropertyDescriptor property : propertyDescriptors) {
				String properName = property.getName();
				if (map.containsKey(property.getName())) {
					try {// 不影响其他属性
						Object value = map.get(properName);
						Method setter = property.getWriteMethod();
						setter.invoke(obj, value);
					} catch (Exception e) {
						// e.printStackTrace();
					}

				}
			}
		} catch (Exception e) {
			e.printStackTrace();
			return obj;
		}
		return obj;
	}

	// jdk:
	// 10000000次4089 4114 4113 4092 4157 4103 4163 4186 4196 4211 4160 4121
	// 1000000次 624 461 443 435 413 421 400 426 409 400 417 404
	// 100000次229 84 64 47 43 51 39 35 37 43 54 49
	// 10000次 91 33 29 22 15 12 5 6 18 9 33 9
	// 1000次 47 7 3 4 8 2 1 3 5 3 3 3
	// -------------------------------------------------------
	// reflectasm
	// 10000000次 5065 4849 4940 4818 4934 4784 4773 4739 4814 4827 4793 4774
	// 1000000次 713 510 529 454 458 468 451 463 450 472 460 455
	// 100000次218 92 76 50 49 61 45 47 42 56 64 59
	// 10000次 85 28 25 27 20 21 7 6 20 13 28 15
	// 1000次47 4 3 6 3 2 2 3 3 8 3 2
	@SuppressWarnings("rawtypes")
	public static Object convertMapToBeanByRefAsm(Map map, Class<?> type) {
		Object obj = null;
		try {
			BeanInfo beanInfo = null;
			MethodAccess access = getFromBeanMethodcache(type);
			beanInfo = Introspector.getBeanInfo(type);
			obj = type.newInstance();
			PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
			for (PropertyDescriptor property : propertyDescriptors) {
				String properName = property.getName();
				if (map.containsKey(property.getName())) {
					try {// 不影响其他属性
						Object value = map.get(properName);
						Method setter = property.getWriteMethod();
						access.invoke(obj, setter.getName(), value);
					} catch (Exception e) {
					}
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
			return obj;
		}
		return obj;
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	public static Map convertBeanToMapByRefAsm(Object bean) {
		Map returnMap = null;
		try {
			Class<?> type = bean.getClass();
			BeanInfo beanInfo = Introspector.getBeanInfo(type);
			PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
			MethodAccess access = getFromBeanMethodcache(type);
			returnMap = new HashMap();
			for (PropertyDescriptor property : propertyDescriptors) {
				String properName = property.getName();
				Method getter = property.getReadMethod();
				try {
					Object value = access.invoke(bean, getter.getName());
					returnMap.put(properName, value);
				} catch (Exception e) {
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return returnMap;
	}

	public static Map<String, Object> convertBeanToMap(Object bean) {
		Map<String, Object> returnMap = null;
		try {
			Class<?> type = bean.getClass();
			BeanInfo beanInfo = Introspector.getBeanInfo(type);
			PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
			returnMap = new HashMap<String, Object>();
			for (PropertyDescriptor property : propertyDescriptors) {
				String properName = property.getName();
				if ("class".equals(properName)) {
					continue;
				}
				Method getter = property.getReadMethod();
				try {
					Object value = getter.invoke(bean);
					returnMap.put(properName, value);
				} catch (Exception e) {
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return returnMap;
	}

	private static ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
	private static Validator validator = factory.getValidator();

	public static void validateBean(Object bean) {
		if (bean == null) {
			RKAlert.Error("参数错误");
		}
		Class<?> type = bean.getClass();
		BeanInfo beanInfo = null;
		try {
			beanInfo = Introspector.getBeanInfo(type);
		} catch (IntrospectionException e) {
			// e.printStackTrace();
		}
		// Set<?> validateResx=validator.validate(bean);
		// for(Object validateResult:validateResx){
		// String message=((ConstraintViolation<?>)validateResult).getMessage();
		// ZAlert.Error(message);
		// }
		PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
		for (PropertyDescriptor property : propertyDescriptors) {
			String properName = property.getName();
			if ("class".equals(properName)) {
				continue;
			}
			Set<?> validateRes = validator.validateProperty(bean, properName);
			if (validateRes == null || validateRes.isEmpty()) {
				continue;
			}
			for (Object validateResult : validateRes) {
				String message = ((ConstraintViolation<?>) validateResult).getMessage();
				RKAlert.Error(message);
			}

		}
	}
	/**
	 * @author:cavion
	 * @描述：将实体转换成map函数
	 * 2016年10月26日 下午9:29:56
	 * @param obj
	 * @return
	 */
	public static Map<String, String> ConvertBeanToMap(Object obj) {
		Map<String, String> reMap = new HashMap<String, String>();
		if (obj == null){
			return null;
		}
		Field[] fields = obj.getClass().getDeclaredFields();
		try {
			for (int i = 0; i < fields.length; i++) {
				try {
					Field f = obj.getClass().getDeclaredField(fields[i].getName());
					f.setAccessible(true);
					String o = String.valueOf(f.get(obj));
					reMap.put(fields[i].getName(), o.trim());
				} catch (NoSuchFieldException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (IllegalArgumentException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (IllegalAccessException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		} catch (SecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return reMap;
	}
}
